#include "TreePrinter.h"
#include "../../BTree/RegistroIndice.h"


TreePrinter::TreePrinter(BTree& arbol) : _arbol(arbol) {
}

TreePrinter::~TreePrinter() {
}


void TreePrinter::ImprimirEnPreOrden(std::string nomArch) const {

	// Carga la raiz usando los datos del árbol
	int posRaiz = this->_arbol._cabecera.getPosBloqueRaiz();
	int altura = this->_arbol._cabecera.getAlturaArbol();

	INodo* nRaiz = this->_arbol.cargarNodo(posRaiz, altura);

	// El flujo de salida
	std::ofstream fout;
	std::ostream& out = abrirOutStream(nomArch, fout);

	// Ejecuta el recorrido en pre-orden
	preOrden(out, *nRaiz, altura, posRaiz);

	delete nRaiz;
	cerrarOutStream(fout);
}


// Recorre en pre-orden los nodos del árbol
void TreePrinter::preOrden(std::ostream& out, INodo& n, int altura, int posicion) const {
	// Visita el nodo, en este caso, lo imprime
	out << getSangria(altura)
			<< posicion << ":" << altura
			<< n << std::endl;

	// Si es nodo hoja acá termina el recorrido
	if (altura == 0) return;

	// Un registro vacio deberia la menor clave imposible y la busqueda inexacta
	// trae al mayor inmediato, por lo que deberia traer el primer registro del nodo
	RegistroIndice ri;
	ri = n.buscarRegistroPorId(ri, false);

	std::string claveAnterior;
	do {
		int pos = ri.getPosHijo();
		INodo* nHijo = this->_arbol.cargarNodo(pos, altura-1);
		preOrden(out, *nHijo, altura-1, pos);

		// El proximo registro del nodo
		delete nHijo;
		claveAnterior = ri.getClave();
		ri = n.buscarRegistroPorId(ri, false);

	} while (ri.getClave() > claveAnterior);

	/* El recorrido en pre-orden es:
	 *
	 * preorder(node)
	 * 	if node == null then return
	 * 	visit(node)
	 * 	preorder(node.left)
	 * 	preorder(node.right)
	 */
}


// Genera una sangria para que la impresión quede bonita
std::string TreePrinter::getSangria(int altura) const{

	// La cantidad de tabs que hay que colocar
	std::string sangria = "";
	int cantTabs = this->_arbol._cabecera.getAlturaArbol() - altura;
	for (int i = 0; i < cantTabs; ++i){ sangria += "\t"; }

	return sangria;
}


// Imprime los nodos hojas del árbol
void TreePrinter::ImprimirDatos(std::string nomArch) const {

	// Abre el archivo si se especificó alguno
	std::ofstream fout;
	std::ostream& out = abrirOutStream(nomArch, fout);

	// El primer registro de datos
	RegistroDatos rSec = this->_arbol.proximoRegistroPorId();

	// Recorre los registros y los va imprimiendo
	std::string claveAnterior;
	do {
		out << rSec.getClave() << std::endl;

		claveAnterior = rSec.getClave();
		rSec = this->_arbol.proximoRegistroPorId(rSec);

	} while (rSec.getClave() > claveAnterior);

	cerrarOutStream(fout);
}


// Abre un stream de salida, consola o archivo según si se definió el nombre del archivo o no
std::ostream& TreePrinter::abrirOutStream(std::string nomArch, std::ofstream& fout) const {

	// Si no hay un nombre de archivo, la salida es por consola
	if (nomArch == "") return std::cout;

	// La salida es por archivo
	fout.open( nomArch.c_str() );
	return fout;
}


// Se cierra el archivo, solo si es necesario
void TreePrinter::cerrarOutStream(std::ofstream& fout) const {
	if (fout.is_open()){
		fout.close();
	}
}
